/*
 * Email Notify
 * Copyright: (C) 2008 by Antonio Nastasi
 * Email: sifcenter (at) gmail.it
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "emailchecker.h"

#include <QStringList>
#include <KDebug>

#define INFORMATION_DEBUG

EmailChecker::EmailChecker( QObject *parent ) :
  QThread(parent),
  account_(0),
  socket_(0)
{
  kDebug() << "Init";
  connect( this, SIGNAL( finished() ), this, SLOT( threadFinished() ) );
}



void EmailChecker::threadFinished()
{
    kDebug() << "Finished thread";
    state = NO_CONNECTED;
}



EmailChecker::~EmailChecker()
{
  exit();
  delete socket_;
  kDebug() << "DESTROYED";
}



void EmailChecker::run()
{
  if( socket_ == 0 )
  {
    socket_ = new QSslSocket();
    Q_ASSERT( socket_ != 0 );

    // Connect the socket_ signals
    connect( socket_, SIGNAL( encrypted() ), this, SLOT( socketEncrypted() ) );
    connect( socket_, SIGNAL( connected() ), this, SLOT( socketConnected() ) );
    connect( socket_, SIGNAL( readyRead() ), this, SLOT( getMessage() ) );
    connect( socket_, SIGNAL(           error( QAbstractSocket::SocketError ) ),
             this,      SLOT( errorConnection( QAbstractSocket::SocketError ) ) );
  }
  else
  {
    kDebug() << "We already have the pointer!";
    socket_->abort();
  }

  state = NO_CONNECTED;

#ifdef INFORMATION_DEBUG
  kDebug() << "server:" << account_->getServer();
  kDebug() << "port:" << account_->getPort();
  kDebug() << "ssl:" << account_->isSSL();
  kDebug() << "protocol:" << account_->getProtocol();
#endif

  // Connect with encryption if in account there is ssl enabled
  if( account_->isSSL() )
  {
    socket_->connectToHostEncrypted( account_->getServer() ,account_->getPort() );
  }
  else
  {
    socket_->connectToHost( account_->getServer(), account_->getPort() );
  }

  kDebug() << "Try to connect, ssl:" << account_->isSSL();

  // Wait for 6 seconds, and put the error if there is no connection
  if( socket_->waitForConnected( 6000 ) )
  {
    kDebug() << "wait for connection: TRUE";
  }
  else
  {
    kDebug() << "wait for connection: FALSE";
    closeConnection();
    updateText( "Connection Error" );
    return;
  }

  exec();
}



void EmailChecker::closeConnection()
{
  // Close connection;
  if( socket_ != 0 )
  {
    socket_->abort();
  }

  state = NO_CONNECTED;
  kDebug() << "Closing connection...";

  // Terminate the thread
  exit();
}



void EmailChecker::socketEncrypted()
{
  kDebug() << "Socket Encrypted";
  state = CONNECTED;
}



void EmailChecker::socketConnected()
{
  kDebug() << "Socket Connected";

  // If the account not require the SSL, then set the CONNECTED state
  if( ! account_->isSSL() )
  {
    state = CONNECTED;
  }
}



void EmailChecker::setAccount( Account *account )
{
  account_ = account;
}



// Send message trought the socket_
void EmailChecker::sendMessage( CommandType type )
{
  QString buffer;

  switch( type )
  {
    case LOGIN:
    {
      const QString& login( account_->getLogin() );
      if( account_->getProtocol() == "POP3" )
      {
        buffer = "USER " + login;
      }
      else
      {
        buffer = ". login " + login + " " + account_->getPassword();
      }

      state = VERIFY_USER;
    }
      break;

    case PASSWORD:
      buffer = "PASS " + account_->getPassword();
      state = VERIFY_PASSWORD;
      break;

    case STAT:
      if( account_->getProtocol() == "POP3" )
      {
        buffer = "STAT";
      }
      else
      {
        buffer = ". STATUS INBOX (UNSEEN)";
      }

      state = CMD_STAT;
      break;

    default:
      closeConnection();
      return;
      break;
  }

  if( socket_->write( QString( buffer + "\r\n").toUtf8() ) == -1 )
  {
    kDebug() << "Error while socket writing...";
    closeConnection();
    return;
  }

  // Force to flush the buffer to avoid problems
  // with next first sync
  socket_->flush();

#ifdef INFORMATION_DEBUG
  kDebug() << "Buffer out:" << buffer.replace( QChar( '\n' ) , QChar() );
  kDebug() << "State changed to:" << state;
#endif
}



// Read the data from socket_
void EmailChecker::getMessage()
{
  // Read the entire buffer
  const QByteArray& buffer( socket_->readAll() );

#ifdef INFORMATION_DEBUG
  kDebug() << "Buffer in:" << QString( buffer ).replace( QChar( '\n' ) , QChar() );
  kDebug() << "Current State:" << state;
#endif

  if( buffer.contains( "OK" ) )
  {
    switch( state )
    {
      case CONNECTED:
        kDebug() << "Protocol OK: connected!";
        sendMessage( LOGIN );
        break;

      case NO_CONNECTED:
        kDebug() << "Code error";
        break;

      case VERIFY_USER:
        kDebug() << "Verify user: user accepted";
        if( account_->getProtocol() == "POP3" )
        {
          sendMessage( PASSWORD );
        }
        else
        {
          sendMessage( STAT );
        }

        break;

      case VERIFY_PASSWORD:
        kDebug() << "Verify password: password accepted";
        sendMessage( STAT );
        break;

      case CMD_STAT:
        kDebug() << "Command Stat";
        QString emailsText;
        int emails;

        if( account_->getProtocol() == "POP3" )
        {
          QStringList temp1 = QString(buffer).split(" ");
          emailsText = temp1.at( 1 );
          emails = temp1.at( 1 ).toInt();
        }
        else
        {
          if( ! buffer.contains("UNSEEN") )
          {
            updateText( "Not supported: imap haven't UNSEEN" );
            closeConnection();
            break;
          }
          const QStringList&  temp( QString(buffer).split( " " ) );
          const QStringList& temp2( QString( temp[ 4 ] ).split( ")" ) );
          emailsText = QString( temp2[ 0 ] );
          emails = emailsText.toInt();
        }

        account_->setCurrentEmails( emails );
        closeConnection();
        break;
    }
  }
  else if ( buffer.contains( "-ERR" ) || buffer.contains( ". NO" ) || buffer.contains( ". BAD" ) )
  { //TODO more checks
    QString text;
    switch( state )
    {
      case NO_CONNECTED:
        kDebug() << "Code error";
        break;

      case CONNECTED:
      case CMD_STAT:
        kDebug() << "Protocol Error";
        text = "Protocol issues";
        break;

      case VERIFY_USER:
        kDebug() << "Verify user: user NOT accepted";
        text = "Wrong user";
        break;

      case VERIFY_PASSWORD:
        kDebug() << "Verify password: password NOT accepted";
        text = "Authentication failed";
        break;
    }
    closeConnection();
    updateText( text );
  }
}



void EmailChecker::updateText( QString text )
{
  kDebug() << "Text updated:" << text;
  emit updatedText( text );
}



void EmailChecker::errorConnection( QAbstractSocket::SocketError error )
{
  kDebug() << "Error" << error;
  updateText( "Connection Error" );
  closeConnection();
}
